using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using Etier.IconHelper;
using SharpSvn;
using System.Collections.ObjectModel;
using SubManager.Entity;
using System.Collections;

namespace SubManager.Win
{
    /// <summary>
    /// Explore multiple repository on multiple server.
    /// </summary>
    public partial class RepositoryExplorer : UserControl
    {
        #region Construcotrs
        public RepositoryExplorer()
        {
            InitializeComponent();

            _trvRepositories.ImageList = _smallImageList;
            _iconListManager = new IconListManager(_smallImageList, IconReader.IconSize.Small);
            _svnClient = new SvnClient();
        }
        #endregion

        #region Events
        /// <summary>
        /// Occured after tree node selected.
        /// </summary>
        public event TreeViewEventHandler ItemSelected;
        #endregion

        #region Fields
        private SvnClient _svnClient;
        private IconListManager _iconListManager;
        #endregion

        #region Properties
        
        #endregion

        #region Methods
        /// <summary>
        /// Open a repository node.
        /// </summary>
        /// <param name="reposUrl"></param>
        private void OpenRepos(string reposUrl)
        {
            TreeNode root = new TreeNode(reposUrl);
            BuildRepositoryTree(reposUrl, ref root);

            if (root.Nodes.Count > 0)
            {
                foreach (TreeNode node in root.Nodes)
                {
                    _trvRepositories.SelectedNode.Nodes.Add(node);
                }
                _trvRepositories.SelectedNode.Expand();
            }
        }

        /// <summary>
        /// Close a repository node.
        /// </summary>
        private void CloseRepos()
        {
            if ((_trvRepositories.SelectedNode.Tag is RepositoryEntity) &&
                (_trvRepositories.SelectedNode.Nodes.Count > 0))
                _trvRepositories.SelectedNode.Nodes.Clear();
        }

        /// <summary>
        /// Add a repository node.
        /// </summary>
        private void AddRepos()
        {
            RepositoryUrlForm frm = new RepositoryUrlForm();
            if (DialogResult.OK == frm.ShowDialog())
            {
                TreeNode node = new TreeNode();
                node.Text = frm.Url;
                node.Tag = new RepositoryEntity();
                BuildRepositoryTree(frm.Url, ref node);

                if(node.Nodes.Count > 0)
                    _trvRepositories.Nodes.Add(node);
            }
        }

        /// <summary>
        /// Remove a repository node.
        /// </summary>
        private void RemoveRespos()
        {
            if (DialogResult.OK.Equals(
                MessageBox.Show("Are you sure to remove this repository?",
                "Remove Confirm", MessageBoxButtons.OKCancel)))
            {
                if(_trvRepositories.SelectedNode != null &&
                    _trvRepositories.SelectedNode.Tag is RepositoryEntity)
                    _trvRepositories.Nodes.Remove(_trvRepositories.SelectedNode);
            }
        }
        
        /// <summary>
        /// Collapsed all nodes.
        /// </summary>
        private void CollpaseAll()
        {
            _trvRepositories.CollapseAll();
        }

        /// <summary>
        /// Deep First Traverse.
        /// Considering efficiency, only 2 layer of tree nodes would be retrive.
        /// </summary>
        /// <param name="path">the path</param>
        /// <param name="root">root tree node</param>
        private void BuildRepositoryTree(string path, ref TreeNode parent)
        {
            Collection<SvnListEventArgs> topList = new Collection<SvnListEventArgs>();
            if ((path.Length <= 0) || (!_svnClient.GetList(path, out topList)))
                return;

            foreach (SvnListEventArgs top in topList)
            {
                if (top.Path.Length <= 0)
                    continue;

                TreeNode child = CreateChildNode(parent, top);
                if (RepositoryEntryEnum.Directory ==
                    (child.Tag as RepositoryEntryEntity).EntryType)
                {
                    // Seache first level child nodes.
                    Collection<SvnListEventArgs> subList = new Collection<SvnListEventArgs>();
                    if (!_svnClient.GetList(path + "/" + top.Path, out subList))
                        continue;

                    foreach (SvnListEventArgs sub in subList)
                    {
                        if (sub.Path.Length <= 0)
                            continue;

                        CreateChildNode(child, sub);
                    }
                }
            }

            SetTreeImages(parent);
        }

        /// <summary>
        /// Sort sub nodes alphabetic.
        /// </summary>
        /// <param name="subNodes"></param>
        private void SortNodes(TreeNodeCollection subNodes)
        {
            if (subNodes.Count <= 1)
                return;

            List<TreeNode> folders = new List<TreeNode>();
            List<TreeNode> files = new List<TreeNode>();

            foreach (TreeNode node in subNodes)
            {
                if (node.Tag != null)
                {
                    RepositoryEntryEntity entity = (node.Tag as RepositoryEntryEntity);
                    if (null == entity)
                        continue;

                    if (entity.EntryType == RepositoryEntryEnum.File)
                    {
                        files.Add(node);
                    }
                    else
                    {
                        folders.Add(node);
                    }
                }
            }

            folders.Sort(delegate(TreeNode l, TreeNode r)
            {
                return Comparer<string>.Default.Compare(l.Text.ToLower(), r.Text.ToLower());
            });

            files.Sort(delegate(TreeNode l, TreeNode r)
            {
                return Comparer<string>.Default.Compare(l.Text.ToLower(), r.Text.ToLower());
            });

            subNodes.Clear();
            Array.ForEach<TreeNode>(folders.ToArray(), delegate(TreeNode node)
            {
                subNodes.Add(node);
            });

            Array.ForEach<TreeNode>(files.ToArray(), delegate(TreeNode node)
            {
                subNodes.Add(node);
            });
        }

        /// <summary>
        /// Create child tree node.
        /// </summary>
        /// <param name="parent"></param>
        /// <param name="top"></param>
        /// <returns></returns>
        private TreeNode CreateChildNode(TreeNode parent, SvnListEventArgs top)
        {
            RepositoryEntryEntity entry = new RepositoryEntryEntity();
            
            TreeNode child = parent.Nodes.Add(top.Path);
            child.Tag = entry;

            entry.EntryUrl = child.Text;
            switch (top.Entry.NodeKind)
            {
                case SvnNodeKind.Directory:
                    entry.EntryType = RepositoryEntryEnum.Directory;
                    break;
                case SvnNodeKind.File:
                    entry.EntryType = RepositoryEntryEnum.File;
                    break;
                case SvnNodeKind.None:
                    entry.EntryType = RepositoryEntryEnum.Repository;
                    break;
                case SvnNodeKind.Unknown:
                    entry.EntryType = RepositoryEntryEnum.Unknown;
                    break;
                default:
                    entry.EntryType = RepositoryEntryEnum.Unknown;
                    break;
            }
            
            return child;
        }

        /// <summary>
        /// Set image for every TreeNode.
        /// </summary>
        /// <param name="path">Full path of tree node.</param>
        /// <returns>The index of tree node.</returns>
        private void SetTreeImages(TreeNode root)
        {
            SetNodeImage(root);

            foreach (TreeNode node in root.Nodes)
            {
                SetTreeImages(node);
            }
        }

        /// <summary>
        ///  Set image for a TreeNode.
        /// </summary>
        /// <param name="node"></param>
        private void SetNodeImage(TreeNode node)
        {
            if (!(node.Tag is RepositoryEntryEntity))
                return;

            if ((node.Tag as RepositoryEntryEntity).EntryType == RepositoryEntryEnum.Directory)
            {
                node.ImageIndex = _iconListManager.TryGetFolderIcon(
                   IconReader.IconSize.Small, IconReader.FolderType.Closed);
                node.SelectedImageIndex = _iconListManager.TryGetFolderIcon(
                       IconReader.IconSize.Small, IconReader.FolderType.Open);
            }
            else
            {
                if (node.Parent != null)
                {
                    node.ImageIndex = _iconListManager.TryGetFileIcon(node.Parent.Text + "/" + node.Text);
                    node.SelectedImageIndex = 
                        _iconListManager.TryGetFileIcon(node.Parent.Text + "/" + node.Text);
                }
                else
                {
                    node.ImageIndex = _iconListManager.TryGetFileIcon(node.Text);
                    node.SelectedImageIndex = _iconListManager.TryGetFileIcon(node.Text);
                }
            }
        }

        #endregion

        #region Event Handlers
        private void _btnAddRepos_Click(object sender, EventArgs e)
        {
            AddRepos();
        }

        private void _btnRemoveRepos_Click(object sender, EventArgs e)
        {
            RemoveRespos();
        }

        private void _btnOpenRepos_Click(object sender, EventArgs e)
        {
            if( ((_trvRepositories.SelectedNode.Nodes == null) 
                || _trvRepositories.SelectedNode.Nodes.Count <= 0) &&
                (_trvRepositories.SelectedNode.Tag is RepositoryEntity) )
                OpenRepos(_trvRepositories.SelectedNode.Text);
        }

        private void _btnCloseRepos_Click(object sender, EventArgs e)
        {
            CloseRepos();
        }

        private void _btnCollapseAll_Click(object sender, EventArgs e)
        {
            CollpaseAll();
        }

        private void _tsmiOpenRepos_Click(object sender, EventArgs e)
        {
            if (((_trvRepositories.SelectedNode.Nodes == null)
                || _trvRepositories.SelectedNode.Nodes.Count <= 0) &&
                (_trvRepositories.SelectedNode.Tag is RepositoryEntity))
                OpenRepos(_trvRepositories.SelectedNode.Text);
        }

        private void _tsmiCloseRepos_Click(object sender, EventArgs e)
        {
            CloseRepos();
        }

        private void _tsmiCollapseAll_Click(object sender, EventArgs e)
        {
            CollpaseAll();
        }

        private void _tsmiAddRepos_Click(object sender, EventArgs e)
        {
            AddRepos();
        }

        private void _tsmiRemoveRepos_Click(object sender, EventArgs e)
        {
            RemoveRespos();
        }

        private void _repositoryTreeView_AfterExpand(object sender, TreeViewEventArgs e)
        {
            SortNodes(e.Node.Nodes);

            if (e.Node.Nodes.Count > 0)
            {
                e.Node.ImageIndex = _iconListManager.TryGetFolderIcon(
                    IconReader.IconSize.Small, IconReader.FolderType.Open);

                // return if any child already has children.
                foreach (TreeNode node in e.Node.Nodes)
                {
                    if (node.Nodes.Count > 0)
                    {
                        return;
                    }
                }
            }

            string currentPath = e.Node.FullPath;
            TreeNode temp = null;
            foreach (TreeNode sub in e.Node.Nodes)
            {
                temp = sub;
                BuildRepositoryTree(sub.FullPath, ref temp);
            }
        }

        private void _trvRepositories_AfterCollapse(object sender, TreeViewEventArgs e)
        {
            e.Node.ImageIndex =
                _iconListManager.TryGetFolderIcon(IconReader.IconSize.Small, IconReader.FolderType.Closed);
        }

        private void _trvRepositories_AfterSelect(object sender, TreeViewEventArgs e)
        {
            if (ItemSelected != null)
                ItemSelected(sender, e);
        }
        #endregion

        

        
    }
}
